package cn.cosmosx.base.util;

import cn.cosmosx.base.function.PropertyFunc;
import io.swagger.annotations.ApiModel;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.reflections.Reflections;
import org.springframework.util.CollectionUtils;
import cn.cosmosx.base.constant.ResultCode;
import cn.cosmosx.base.exception.PlatformException;

import java.lang.annotation.Annotation;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 类相关工具
 */
@Slf4j
public abstract class ClassUtils {
    private final static Pattern HUMP_PATTERN = Pattern.compile("[A-Z]");
    private final static String OBJECT = "Object";

    /**
     * 获取类描述
     *
     * @param entityClassType
     * @return
     */
    public static String getClassDescribe(Class<?> entityClassType) {
        ApiModel ann = entityClassType.getAnnotation(ApiModel.class);
        return ann == null ? entityClassType.getSimpleName() : ann.description();
    }

    /**
     * 根据字段函数获取字段名称名称
     *
     * @param field 字段
     * @return
     */
    public static <T> String toFieldName(PropertyFunc<T, ?> field) {
        return toFieldName(field.getClass(), field);
    }

    private static String toFieldName(Class<?> clazz, PropertyFunc<?, ?> field) {
        try {
            Method method = clazz.getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda lambda = (SerializedLambda) method.invoke(field);
            return getFieldName(lambda.getImplMethodName());
        } catch (Exception e) {
            if (!OBJECT.equals(clazz.getSuperclass().getSimpleName())) {
                return toFieldName(clazz.getSuperclass(), field);
            }
            log.error("ClassUtils.toFieldName", e);
            throw new PlatformException(ResultCode.E00099, "The field is not table column");
        }
    }

    /**
     * 根据get方法名称获取字段名称
     *
     * @param methodName
     * @return
     */
    public static String getFieldName(String methodName) {
        return StringUtils.uncapitalize(methodName.startsWith("get") ? methodName.substring(3) : (methodName.startsWith("is") ? methodName.substring(2) : methodName));
    }

    /**
     * 获取对象target中字段field的值
     *
     * @param target 对象
     * @param field  字段
     * @return
     */
    public static Object getFieldValue(Object target, Field field) {
        try {
            field.setAccessible(true);
            return field.get(target);
        } catch (Exception e) {
            log.error("ClassUtils.getFieldValue", e);
            throw new PlatformException(ResultCode.E00099, String.format("无法获取字段值:%s.%s", target.getClass().getSimpleName(), field.getName()));
        }
    }

    /**
     * 设置字段值
     *
     * @param target
     * @param field
     * @param value
     */
    public static void setFieldValue(Object target, Field field, Object value) {
        try {
            field.setAccessible(true);
            field.set(target, value);
        } catch (Exception e) {
            log.error("ClassUtils.setFieldValue", e);
            throw new PlatformException(ResultCode.E00099, String.format("无法设置字段值:%s.%s", target.getClass().getSimpleName(), field.getName()));
        }
    }

    /**
     * 获取自定类的方法列表
     *
     * @param cls
     * @return
     */
    public static List<Method> getMethod(Class<?> cls) {
        return LocalCacheUtils.getCache("bean_all_method", cls.getName(), () -> {
            List<Method> allMethods = new ArrayList<>(Arrays.asList(cls.getMethods()));
            Class<?> supCls = cls.getSuperclass();
            while (!OBJECT.equals(supCls.getSimpleName())) {
                if (supCls.getMethods().length <= 0) {
                    continue;
                }
                allMethods.addAll(Arrays.asList(supCls.getMethods()));
                supCls = supCls.getSuperclass();
            }
            return CollectionUtils.isEmpty(allMethods) ? Collections.emptyList() : allMethods;
        });
    }

    /**
     * 根据方法名称获取指定类的方法
     *
     * @param cls
     * @param methodName
     * @return
     */
    public static Method getMethod(Class<?> cls, String methodName) {
        Map<String, Method> methodMap = LocalCacheUtils.getCache("bean_all_method_mapping", cls.getName(), () -> {
            List<Method> methods = getMethod(cls);
            if (CollectionUtils.isEmpty(methods)) {
                return Collections.emptyMap();
            }
            Map<String, Method> mp = new HashMap<>(methods.size());
            methods.forEach(method -> mp.put(method.getName(), method));
            return mp;
        });
        return CollectionUtils.isEmpty(methodMap) ? null : methodMap.get(methodName);
    }


    /**
     * 获取目标类的字段(包含父类)
     *
     * @param cls             目标类
     * @param annClass        只有该注解的字段（如果为空，则获取所有字段）
     * @param annExcludeClass 排除有该注解的字段（如果为空，则不做排除处理）
     * @return
     */
    public static List<Field> getAllFields(Class<?> cls, Class<? extends Annotation> annClass, Class<? extends Annotation> annExcludeClass, boolean LoadSupper) {
        String pre = Objects.isNull(annClass) ? "all_field_" : ("ann_field_" + annClass.getName());
        pre = pre + (LoadSupper ? "1_" : "0_");
        pre = Objects.isNull(annExcludeClass) ? pre : (pre + "exc_" + annExcludeClass.getName());
        return LocalCacheUtils.getCache(pre, cls.getName(), () -> {
            List<Field> fields = new ArrayList<>(Arrays.asList(cls.getDeclaredFields()));
            Class<?> upCls = cls;
            if (LoadSupper) {
                while (!OBJECT.equals(upCls.getSimpleName())) {
                    upCls = upCls.getSuperclass();
                    if (upCls.getDeclaredFields().length <= 0) {
                        continue;
                    }
                    fields.addAll(Arrays.asList(upCls.getDeclaredFields()));
                }
            }
            List<Field> columnFields = new ArrayList<>(fields.size());
            fields.forEach(field -> {
                if (annClass == null || field.isAnnotationPresent(annClass)) {
                    boolean annExclude = annExcludeClass != null && field.isAnnotationPresent(annExcludeClass);
                    if (!annExclude) {
                        columnFields.add(field);
                    }
                }
            });
            return columnFields;
        });
    }

    /**
     * 获取目标类的所有字段(包含父类)
     *
     * @param cls             目标类
     * @param annClass        只有该注解的字段（如果为空，则获取所有字段）
     * @param annExcludeClass 排除有该注解的字段（如果为空，则不做排除处理）
     * @return
     */
    public static List<Field> getAllColumnFields(Class<?> cls, Class<? extends Annotation> annClass, Class<? extends Annotation> annExcludeClass) {
        return getAllFields(cls, annClass, annExcludeClass, true);
    }


    /**
     * 根据字段函数获取类的简单名称
     *
     * @param field 字段
     * @return
     */
    public static String getClassName(PropertyFunc<?, ?> field) {
        try {
            Method method = field.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            SerializedLambda lambda = (SerializedLambda) method.invoke(field);
            String[] packages = lambda.getInstantiatedMethodType().split("\\)")[0].replaceAll(";", "").split("/");
            return packages[packages.length - 1];
        } catch (ReflectiveOperationException e) {
            log.error("ClassUtils.getClassName", e);
            throw new PlatformException(ResultCode.E00099, "get class name error!");
        }
    }

    /**
     * 根据字段函数获取类的简单名称
     *
     * @param field 字段
     * @return
     */
    public static String getClassNameByField(Field field) {
        return field.getDeclaringClass().getSimpleName();
    }

    /**
     * 加载包prefix路径下的继承了cls类的所有子类
     *
     * @param prefix 包路径
     * @param cls    父类类型
     * @param <T>
     * @return
     */
    public static <T> Set<Class<? extends T>> load(String prefix, Class<T> cls) {
        Reflections reflections = new Reflections(prefix);
        return reflections.getSubTypesOf(cls);
    }

    /**
     * 根据字段名称获取指定类中的字段
     *
     * @param cls
     * @param fieldName
     * @return
     */
    public static Field getField(Class<?> cls, String fieldName) {
        Map<String, Field> mapField = LocalCacheUtils.getCache("bean_all_field", cls.getName(), () -> {
            List<Field> fields = ClassUtils.getAllFields(cls, null, null, true);
            if (CollectionUtils.isEmpty(fields)) {
                return Collections.emptyMap();
            }
            Map<String, Field> mapFields = new HashMap<>(fields.size());
            fields.forEach(field -> mapFields.put(field.getName(), field));
            return mapFields;
        });
        return CollectionUtils.isEmpty(mapField) ? null : mapField.get(fieldName);
    }

    public static <T> Type getGenericType(Class<T> currentType, Class<?> targetCls) {
        Type[] interfaces = currentType.getGenericInterfaces();
        for (Type type : interfaces) {
            if (type instanceof ParameterizedType) {
                ParameterizedType pType = (ParameterizedType) type;
                if (pType.getRawType() == targetCls) {
                    return pType.getActualTypeArguments()[0];
                }
            }
        }
        throw new IllegalArgumentException("Enum " + currentType.getName() + " does not implement " + targetCls.getName());
    }
}
